#pragma once

#include "common/common.h"
#include "string/stringmanipulations.h"
#include "string/stringoutput.h"

namespace PILO 
{
    namespace Core
    {
        namespace String
        {
            template<typename T, size_t maxCapacity> class TCFixedString
            {
            public:
                //default destructor
                ~TCFixedString() {};

                //default constructor
                TCFixedString():_m_size(0) {
                    if (maxCapacity > MC_LIMIT_TCFIXEDSTRING_MAX_LENGTH) {
                        throw EC_REACH_UPPER_LIMIT;
                    }
                    _m_data[0] = 0;
                }
                //constructor 
                TCFixedString(const T* s, int_r len)
                {
                    if (maxCapacity > MC_LIMIT_TCFIXEDSTRING_MAX_LENGTH) {
                        throw EC_REACH_UPPER_LIMIT;
                    }
                    if (len >= maxCapacity) {
                        len = maxCapacity-1;
                    }
                    if (StringCopy(_m_data, maxCapacity, s, len) == MC_NULLPTR_OF_(T*)) {
                        _m_size = MC_INVALID_SIZE;
                    } else {
                        _m_size = len;
                    }
                }

                //copy constructor
                TCFixedString(const TCFixedString& original):_m_size(original._m_size)
                {
                    if(_m_size > 0) {
                        StringCopy(_m_data, maxCapacity, original._m_data, _m_size);
                    }
                    _m_data[_m_size] = 0;
                }

                //return current size of content
                size_t size() const
                {
                    return _m_size;
                }

                //return if it is empty TriState.EnumNA for error
                TriState empty() const
                {
                    if (_m_size == 0) {
                        return EnumTriStateYes;
                    } else if (_m_size == MC_INVALID_SIZE) {
                        return EnumTriStateNA;
                    }
                    return EnumTriStateNo;
                }


                //clear this content, but not reset all bit to zero
                TCFixedString& clear()
                {
                    _m_size = 0;
                    _m_data[_m_size] = 0;
                    return *this;
                }


                //return the char * string
                T * data()
                {
                    return &_m_data[0];
                }

                //update the length after set data directly
                size_t updateSize()
                {
                    _m_size = StringLength(_m_data);
                    return _m_size;
                }

                //return the char * string, const version
                const T* constData() const
                {
                    return &_m_data[0];
                }

                //to judge is it valid
                int_r isValid() const 
                {
                    return (_m_size != MC_INVALID_SIZE);
                }

                //output to console
                void output() const
                {
                    if (!isValid()) {
                        FormatOutputToConsole(TEXT("[Invalid TCFixedString Object @ %x]"), this);
                    } else {
                        if (this->empty()) {
                            FormatOutputToConsole(TEXT("[Empty TCFixedString Object @ %x]"), this);
                        }
                    }
                    FormatOutputToConsole(_m_data);
                }

                //make this a invalid object
                TCFixedString& makeInvalid() 
                {
                    _m_size = MC_INVALID_SIZE;
                    return (*this);
                }

                TCFixedString& makeEmpty() 
                {
                    _m_size = 0;
                    _m_data[0] = 0;
                    return (*this);
                }

                //copy operator
                TCFixedString& operator= (const TCFixedString& original)
                {
                    if(this == &original) {
                        return *this;
                    }

                    if (! original.isValid()) {
                        return this->makeInvalid();

                    } else if (original._m_size > 0) {
                        ASSERT(maxCapacity > original._m_size);
                        if (StringCopy(_m_data, maxCapacity, original._m_data,original._m_size) == MC_NULLPTR_OF_(T*)) {
                            return this->makeInvalid();
                        }
                    }

                    _m_size = original._m_size;
                    _m_data[_m_size] = 0;

                    return *this;
                }

                //copy operator, copy from char * 
                TCFixedString& operator= (const T* str)
                {
                    this->clear();

                    int_r strSourceLength = StringLength(str);
                    if (strSourceLength>0) {
                        if (strSourceLength >= maxCapacity) {
                            strSourceLength = maxCapacity - 1;
                        }
                        if (StringCopy(_m_data,maxCapacity, str,strSourceLength) == MC_NULLPTR_OF_(T*)) {
                            return this->makeInvalid();
                        }
                        _m_size = strSourceLength;
                        _m_data[_m_size] = 0;
                        return *this;
                    }

                    return this->makeEmpty();

                }

                //max characters that can hold 
                size_t capacity() const
                {
                    return maxCapacity;
                }

                //copy a length of memory to internal data
                TCFixedString& assign(const T* str, size_t len)
                {
                    if (len == MC_INVALID_SIZE) {
                        throw EC_OUT_OF_RANGE;
                    }

                    if (len >= maxCapacity) {
                        len = maxCapacity-1;
                    }
                    if (StringCopy(_m_data,maxCapacity, str, len) == MC_NULLPTR_OF_(T*)) {
                        this->makeInvalid();
                    } else {
                        _m_size = len;
                    }

                    return *this;
                }

                //has value at index 1:has value 0:novalue -1:error
                TriState hasValueAt(size_t index) 
                {
                    if (index >= maxCapacity) {
                        return EnumTriStateNA;
                    }

                    if (index >= _m_size)
                    {
                        return EnumTriStateNo;
                    }
                    return EnumTriStateYes;
                } 

                T& operator[] (size_t index)
                {
                    ASSERT(index<maxCapacity);
                    if (index >= maxCapacity) {
                        throw EC_REACH_UPPER_LIMIT;
                    }                

                    if(index >= _m_size) {
                        _m_data[index] = 0;
                    }
                    return _m_data[index];
                }

                // at 
                const T& at(int_r index) const
                {
                    ASSERT(index<maxCapacity);
                    if (index >= maxCapacity) {
                        throw EC_REACH_UPPER_LIMIT;
                    }

                    return static_cast<const T&>(_m_data[index]);
                }

                //format internal string.
                TCFixedString& format( const T * fmt, ... )
                {
                    ASSERT(fmt!= NULL);
                    va_list args;
                    size_t length;

                    va_start(args, fmt);
                    length = VaArgsFormat(_m_data, maxCapacity ,fmt, args);
                    va_end(args);

                    if(length != MC_INVALID_SIZE) {
                        _m_size = length;
                        _m_data[_m_size] = 0;
                    } else {
                        this->makeInvalid();
                    }

                    return (*this);
                }

                //compare fucntions
                INT32 compare(const T * str, int_r length = MC_INVALID_SIZE) const
                {
                    if ((MC_NULLPTR_OF_(T*) == str)) {
                        if (this->empty() != EnumTriStateNo) {
                            return 0;
                        } else {
                            return 1;
                        }
                    }

                    if (length > 0) {
                        return StringCompare(_m_data, str, length);
                    } else {
                        return StringCompare(_m_data, str);
                    }
                }

                INT32 compare (const TCFixedString & right) const
                {
                    return this->compare(right._m_data, right._m_size);
                }

                INT32 caseIgnoredCompare(const T * str, int_r length = MC_INVALID_SIZE) const
                {
                    if ((MC_NULLPTR_OF_(T*) == str)) {
                        if (this->empty() != EnumTriStateNo) {
                            return 0;
                        } else {
                            return 1;
                        }
                    }

                    if (length > 0) {
                        return StringCaseIgnoredCompare(_m_data, str, length);
                    } else {
                        return StringCaseIgnoredCompare(_m_data, str);
                    }
                }

                INT32 caseIgnoredCompare (const TCFixedString & right) const
                {
                    return this->caseIgnoredCompare(right._m_data, right._m_size);
                }

                //compare operators
                BOOL operator <  (const TCFixedString& o) const
                {
                    return this->compare(o) < 0;
                }
                BOOL operator >  (const TCFixedString& o) const
                {
                    return this->compare(o) > 0;
                }
                BOOL operator == (const TCFixedString& o) const
                {
                    return this->compare(o) == 0;
                }
                BOOL operator != (const TCFixedString& o) const
                {
                    return this->compare(o) != 0;
                }
                BOOL operator <= (const TCFixedString& o) const
                {
                    return this->compare(o) <= 0;
                }
                BOOL operator >= (const TCFixedString& o) const
                {
                    return this->compare(o) >= 0;
                }

                TCFixedString& leftTrim(size_t length = MC_INVALID_SIZE)
                {
                    StringTrimLeft(_m_data, length);
                    return *this;
                } 
                TCFixedString& rightTrim(size_t length = MC_INVALID_SIZE)
                {
                    StringTrimRight(_m_data, length);
                    return *this;
                }
                TCFixedString& trim(size_t length = MC_INVALID_SIZE)
                {
                    StringTrim(_m_data, length);
                    return *this;
                }

                TCFixedString& appendString(const T * str, size_t length)
                {
                    size_t retLength = StringAppendString(_m_data, _m_size, maxCapacity, str, length);
                    if (retLength == MC_INVALID_SIZE) { //error occured
                        return this->makeInvalid();
                    } 
                    if (_m_size != retLength) {
                        _m_size = retLength;
                    }

                }

                TCFixedString& append(const T* fmt, T value)
                {
                    size_t retLength = StringAppendFormatted(_m_data,_m_size, maxCapacity, fmt, value);
                    if (retLength == MC_INVALID_SIZE) { //error occured
                        return this->makeInvalid();
                    } 
                    if (_m_size != retLength) {
                        _m_size = retLength;
                    }
                    return *this;
                }

            protected:
                size_t _m_size;
                T _m_data[maxCapacity];
            };

        } /* Containers */
    }
} /* PILO */
